简介

DOM描述了一个层次化的节点树,允许开发人员添加、移除和修改页面。

节点

根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点:

  • 整个文档是一个文档节点
  • 每个 HTML 元素是元素节点
  • HTML 元素内的文本是文本节点
  • 每个 HTML 属性是属性节点
  • 注释是注释节点

每个节点都有一个nodeType属性,用于表明节点的类型。并不是所有节点类型都受到Web浏览器支持,最常用的事元素和文本节点。

Node.ELEMENT_NODE(1); // 元素
Node.ATTRIBUTE_NODE(2); // 属性
Node.TEXT_NODE(3); // 文本
Node.CDATA_SECTION_NODE(4);
Node.ENTITY_REFERENCE_NODE(5);
Node.ENTITY_NODE(6);
Node.PROCESSING_INSTRUCTION_NODE(7);
Node.COMMENT_NODE(8); // 注释
Node.DOCUMENT_NODE(9); // 文档
Node.DOCUMENT_TYPE_NODE(10);
Node.DOCUMENT_FRAGMENT_NODE(11);
Node.NOTATION_NODE(12);

因为IE没有公开Node类型的构造函数,所以确定节点类型如下:

// nodeType 是只读的
if (someNode.nodeType == 1) {
    alert("Node is an element");
}

Node类型

1、nodeName 和 nodeValue 属性

// 使用前先检查节点类型,确认是否是一个元素,对于元素节点
// nodeName 保存的是标签名(节点的名称),nodeValue = null (节点的值);
if (someNode.nodeType == 1) {
    value = someNode.nodeName;    // nodeName的值是元素的标签名
}

2、 节点关系

每个节点都有一个 childNodes 属性,保存着一个 NodeList 对象。

  1. 是一个数组对象,保存一组有序的节点,可以通过位置访问节点。有length属性,但不是数组实例。
  2. 基于DOM结构动态执行查询结果,结构的变化能够自动反应着NodeList对象中。
  3. 可以用 item() 方法访问其中的节点。
var firstChild  = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1); 

// 将NodeList对象转化为数组
function convertToArray(nodes) {
    var array = null;
    try {
        array = Array.prototype.slice.call(node,0);    // 针对非 IE 浏览器
    } catch {
        array = new Array();
        for (var i=0, len=nodes.length; I < len; i++) {
            array.push(nodes[I]);
        }
    }
    return array;
}
  • parentNode 属性:指向文档树中的父节点
  • 父节点的 firstChild 和 lastChild 属性指向第一个和最后一个
  • previousSibling: 上一个同胞节点
  • nextSibling: 下一个同胞节点
  • hasChildNodes(): 节点包含一个或多个子节点返回 true
  • ownerDocument: 指向表示整个文档的文档节点
  • document.documentElement - 全部文档
  • document.body - 文档的主体

3、操作节点

以下方法都需要取得父节点(使用 parentNode 属性)

  • appendChild(): 向 childNodes 列表末尾添加一个节点。如果已存在,从原来位置移动到新位置
  • insertBefore(): 插入节点,接受两个参数,要出入的节点和参考节点(谁的前面)
  • replaceChild(): 替换节点,接受两个参数,要插入的节点和要替换的节点
  • removeChild(): 移除节点

以下两个方法是所有类型的节点都有

  • cloneNode(): 创建节点的副本,参数为true,复制节点及整个子节点树,false只复制节点本身,没有父节点,需要用上面方法添加到文档中
  • normalize(): 处理文档树中的文本节点。可能会出现文本节点不包含文本,或者接连出现两个文本节点。调用这方法,如果找到空文本,则删除;找到相邻文本节点,则合并为一个

Document 类型

JavaScript通过 Document 类型表示文档。在浏览器中,document 对象是 HTMLDocument(继承自 Document 类型)的一个实例,表示整个HTML页面。而且 document 也是 window 对象的一个属性,因此可以将其作为全局对象来访问。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。

  • nodeName 的值为 “#document”;
  • nodeValue 的值为 null;
  • parentNode 的值为 null;
  • ownerDocument 的值为 null;
  • 其子节点可能是一个 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction 或 Comment。

1、文档的子节点

内置的访问子节点的快捷方式:

  • documentElement 属性:始终指向HTML页面的<html>元素
  • childNodes 列表访问文档元素

作为内置的HTMLDocument对象,document 对象有一个 body 属性,直接指向 <body> 元素。

// 所有浏览器都支持
var html = document.documentElement;    // 取得对<html>对引用
var body = document.body;    // 取得对<body>对引用

用不着在 document 对象上调用 appendChild()、removeChild()和replaceChild()方法,因为文档类型(如果存在)是只读的,而且它只能有一个元素子节点(通常早存在)。

2、文档信息

document 对象还有一些标准的Document对象所没有的属性。这些对象表现的网页的一些信息。

// 取的文档标题
var originalTitle = document.title;
// 设置文档标题
document.title = "New page title";

// 取得完成的URL
var url = document.URL;
// 取的域名
var domain = document.domain;
// 取的来源页面的URL
var referrer = document.referrer; 
  • 只有 domain 可以设置,如果包含子域名,不能设置为URL中不包含的域。
  • 由于跨域安全限制,来自不同子域的页面无法通过 Javascript 通信,可以将每个页面的document.domain设置为相同值,就可以互相访问对方包含的JavaScript对象了。
  • 如果域名一开始是“松散的”,就不能设为“紧绷的”,设为“wrox.com”,就不能设为“p2p.wrox.com”。

3、查找元素

取得特定的某个或某组元素的引用,然后执行一些操作。

  • getElementById(): 要获取的元素 id,返回第一个出现的,如果没找到返回 null
  • getElementsByTagName(): 参数(要取得元素的标签名),返回包含零个或多个元素的 NodeList。在HTML文档中,返回一个 HTMLCollection 对象,作为动态集合。传入“*”,取得文档中的所有元素
  • getElementsByName(): 返回给定 name 特性的所有元素。最常用取得单选按钮(单选按钮必须具有相同的 name 特性)
// 取得页面中所有的<img>元素,并返回一个HTMLCollection
var images = document.getElementsByTagName("img");
alert(images.length);    // 图像的数量
alert(images[0].src);    // 第一个图像元素的src特性
alert(images.item(0).src);    // 第一个图像元素的src特性

HMTLCollection 对象

  • 有一个 namedItem() 方法,可以通过元素的 name 特性取得集合中的项
  • 支持按名称访问项
<img src="my image.gif" name="myImage">
var myImage = images.namedItem("myImage");

var myImage = images["myImage"];

4、特殊集合

除了属性和方法,document 对象还有一些特殊的集合,都是 HTMLCollection对象,

  • document.anchors ,包含文档中所有带 name 特性的 <a> 元素;
  • document.applets ,所有的 <applet>元素,不建议使用;
  • document.forms ,文档中所有的 <form> 元素;
  • document.images ,文档中所有的 <img> 元素;
  • document.links ,文档中所有带 href 特性的 <a> 元素。

5、DOM一致性检测

document.implementation 属性提供相应信息和功能的对象,与浏览器对 DOM 的实现直接对应。
规定一个方法:hasFeature(),接受两个参数:要检测的 DOM 功能名称及版本号

var hasXmlDom = document.implementation.hasFeature("XML","1.0");

6、文档写入

  • write(): 原样写入
  • writeln(): 在字符串的末尾添加一个换行符(n)
  • open(): 打开网页的输出流
  • close(): 关闭网页的输出流
// 用 write() 和 written() 动态地包含外部资源
<html>
<head>
    <title>document.write() Example</title>
</head>
<body>
    <script type="text/javascript">
        document.write("<script type=\"text/javascript\" src=\"file.js\">" +
            "<\/script>");
    </script>
</body>

严格型 XHTML 文档不支持文档写入。

Element 类型

Element 类型用于表现 XML 或 HTML 元素,提供了对元素标签名、字节点及特性的访问。

  • nodeType 的值为 1;
  • nodeName 的值为元素的标签名;
  • nodeValue 的值为 null;
  • parentNode 可能是 Document 或 Element;
  • 其子节点可能是 Element、Text、Comment、ProcessingInstruction、CDATASection 或 EntityReference;
  • 访问元素标签名,可以是使用 nodeName 属性或者 tagName 属性。
<div id="myDiv"></div>
var div = document.getElementById("myDiv");
alert(div.tagName);    // "DIV"
alert(div.tagName == div.nodeName);    // true

在 HTML 中,标签名始终都以全部大写表示;而在 XML(有时包括 XHTML),标签名始终与源代码中的保持一致。

if (element.tagName.toLowerCase() == "div") {    // 适用于任何文档
    // ...
}

1、HTML 元素

所有的 HTML 元素都由 HTMLElement 类型表示。每个 HTML 元素都存在的下列标准特性。

  • id,元素中文档中的唯一标识符
  • title,有关元素的附加说明信息,一般通过工具提示条现实出来
  • lang,元素内容的语言代码
  • dir,语言的方向,值为“ltr”(左到右),或“rtl”(右到左)
  • className,与元素的 class 特性对应,即为元素指定的CSS类。(是 ECMAScript 的保留字)
// 上述这些属性都可以用来取得或修改相应的特性值
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
var div = document.getElementById("myDiv");
alert(div.id);
div.id = "someOtherId";
alert(div.className);
div.className = "ft";

2、取得特性

每个元素都有一或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。

  • getAttribute(): 取得特性,传递的特性名与实际的特性名相同,可以是自定义的特性。
  • setAttribute(): 设置特性。设置的特性名统一转为小写。
  • removeAttribute(): 不仅会清除特性值,也会从元素中完成删除特性。

有两类特殊的特性:

  • style: 通过getAttribute()访问返回特性值中包含的文本,通过属性来访问返回一个对象。
  • onclick: 通过 getAttribute()返回相应代码的字符串,访问属性时返回函数。
div.setAttribute("id","someOtherId");

3、attributes 属性

attributes 属性包含一系列节点,每个节点的 nodeName 就是特性的名称,而节点的 nodeValue 就是特性的值。
包含一个 NamedNodeMap,“动态”集合。

  • getNamedItem():返回 nodeName 等于 name 的节点
  • removeNamedItem():从列表中移除 nodeName 属性等于 name 的节点
  • setNamedItem():向类表添加节点,以节点的 nodeName 属性为索引
  • item(pos):返回位于数字pos 的节点
var id = element.attributes.getNamedItem("id").nodeValue;
var id = element.attributes["id"].nodeValue;

遍历元素的特性,attributes 属性可以派上用场。

function outputAttributes(element) {
    var pairs = new Array(),
        attrName,
        attrValue,
        y,
        len;
     
     for(y = 0, len = element.attributes.length; y < len; y++) {
         attrName = element.attributes[y].nodeName;
         attrValue = element.attributes[y].nodeValue;
         if (element.attributes[y].specified) {
             pairs.push(attrName + "=\"" + attrValue + "\"");
         }
     }
     return pairs.join(" ");
}

// attributes 对象的中的特性,不同浏览器返回的顺序不同
// IE7 及更早版本会返回 HTML 元素中所有可能的特性,包括没有指定的特性。每个特性节点都有一个 specified 的属性,true:在 HTML 中指定来相应特性,要么可以通过 setAttribute() 方法设置了该特性。

4、创建元素

// 接受一个参数,即要创建元素的标签名
var div = document.createElement("div");

// 也为新元素设置了 ownerDocument 属性,还可以操作元素的特性,添加更多的子节点
div.id = "myNewDiv";
div.className = "box";

// 新元素添加到文档树中 appendChild()、insertBefore()、replaceChild()
document.body.appendChild(div);

在 IE 中可以另一种方法,传入完整的元素标签

var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div>");

5、元素的子节点

不同浏览器看待节点是不同的。
如果要通过 childNodes 属性遍历子节点,不要忘了浏览器之间的区别,通常先检查一下 nodeType 属性

for (var y = 0, len = element.childNodes.length; y < len; y++) {
    // 表示是元素节点
    if (element.childNodes[y].nodeType == 1) {
        // ...
    }
}

通过特定的标签名取得子节点或后代节点

var ul = document.getElementById("myList");
var items = ul.getElementsByTagName("li");

Text 类型

文本节点由 Text 类型表示,包含纯文本内容,即包含转义后的 HTML 字符,但不包含 HTML 代码。

  • nodeType 的值为 3;
  • nodeName 的值为 “#text”;
  • nodeValue 的值为节点所包含的文本;
  • parentNode 是一个 Element;
  • 不支持(没有)子节点;

通过 nodeValue 属性或 data 属性访问 Text 节点包含的文本。

  • appendData(text):将 text 添加到节点的末尾。
  • deleteData(offset, count):从 offset 指定的位置开始删除 count 个子符。
  • insertData(offset, text):在 offset 指定的位置插入 text。
  • replaceData(offset, count, text):用 text 替换从 offset 指定的位置开始到 offset + count 为止的文本。
  • splitText(offset):从 offset 指定的位置将当前文本节点分为两个文本节点。
  • substringData(offset, count):提取从 offset 指定的位置开始到 offset + count 为止的字符串。

在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。

// 没有内容,也就没有文本节点
<div></div>

// 有空格,因而有一个文本节点
<div> </div>

// 有内容,因而有一个文本节点
<div>hello world!</div>

// 访问,先取得引用
var textNode = div.firstChild;  // 获取 div.childNodes[0]
textNode.nodeValue = "some other message";

在修改文本节点时,此时的字符串会经 HTML 编码(转义)。

1、创建文本节点

  • document.createTextNode():创建文本节点,接受一个参数,要插入的文本,也会进行转义。
var element = document.createElement("div");
element.className = "message";

var textNode = document.createTextNode("Hello world");
element.appendChild(textNode);

var anotherTextNode = document.createTextNode("Yippee");
element.appendChild(anotherTextNode);

document.body.appendChild(element);

如果两个文本节点是相邻的同胞节点,那么这两个节点中的文本会连起来显示,中间不会有空格。

2、规范化文本节点

  • normalize():在一个包含两个或多个文本节点的父元素上调用,则会将所有文本节点合并成一个节点。

Comment 类型

  • nodeType 的值为 8;
  • nodeName 的值为 “#comment”;
  • nodeValue 的值是注释的内容;
  • parentNode 可能是 Document 或 Element;
  • 不支持(没有)子节点。

和 Text 类型相似,可以通过 nodeValue 或 data 属性来取得注释的内容。

<div id="myDiv"><!-- A comment --></div>

// 通过父节点访问
var div = document.getElementById("myDiv"); 
var comment = div.firstChild;
alert(comment.data);

使用 document.createComment() 为其传递注释文本也可以创建注释节点。

var comment = document.createComment("A comment");

如果要访问注释节点,一定要保证他们是 <html> 元素的后代。

DocumentFragment 类型(文档片段)

  • 在文档中没有对应的标记;
  • nodeType 的值为 11;
  • nodeName 的值为 “#document-fragment”;
  • nodeValue 的值为 null;
  • parentNode 的值为 null;

不能把文档片段直接添加到文档中,但可以作为一个“仓库”,保存将来可能添加到文档中的节点。如果将文档中的节点添加到文档片段中,就会从文档树移除该节点,文档片段本身不会成为文档树的一部分。

// 创建文档片段 
var fragment = document.createDocumentFragment();

// 为 ul 元素添加3个列表项
var ul = document.getElementById("myList");
var li = null;

for (var i=0; i < 3; i++) {
    li = document.createElement("li");
    li.appendChild(document.createTextNode("Item " + (i+1)));
    fragment.appendChild(li);
}

ul.appendChild(fragment);

Attr 类型

元素的特性在 DOM 中以 Attr 类型来表示。特性就是存在于元素的 attributes 属性中的节点。

  • nodeType 的值 2;
  • nodeName 的值是特性的名称;
  • nodeValue 的值是特性的值;
  • parentNode 的值为 null;
  • 在 HTML 中不支持子节点;
  • 在 XML 中子节点可以是 Text 或 EntityReference。

尽管是节点,但不是 DOM 文档树但一部分。有3个属性:

  • name:特性名称(与 nodeName 相同);
  • value:特性的值(与 nodeValue 的值相同);
  • specified:一个布尔值,区别特性在代码中是指定的,还是默认的。
// 创建
var attr = document.createAttribute("align");
attr.value = "left";
// 添加到元素中
element.setAttributeNode(attr);

大导演里维斯
15 声望0 粉丝

半个小前端,正在?ing!